﻿#include "session_mgr.h"

#include <future>
#include <iostream>

#include <boost/asio.hpp>
using namespace boost;
using namespace boost::asio;

#include "common/base/config.h"
#include "common/base/config_mgr.h"
#include "common/base/sub_server_mgr.h"
#include "common/base/server_cfg.h"
#include "common/base/server_mgr.h"
#include "common/net/session_mgr.h"
#include "log/log.h"
#include "net/cmd_session.h"
// #include "net/gm_session.h"
#include "net/message_parse.h"
#include "net/session.h"

SessionMgr::SessionMgr(boost::asio::io_service& ios, ServerType serverType)
    : serverType_(serverType)
{
}

bool SessionMgr::initListenNet(const std::shared_ptr<ServerNetInfo>& netInfo) {
    if (!netInfo)
    {
        LOG_ERROR("net info is null.");
        return false;
    }

    if (getListenSession(netInfo->serverType, netInfo->sessionType, netInfo->serverID))
    {
        LOG_DEBUG("Server("  << std::hex << netInfo->getID() << ") already start.");
        return true;
    }

    auto session = ServerMgr::get().getSubServ()->createSession(netInfo->sessionType, shared_from_this(), netInfo, eSessionCreateListen);
    if (!session || !session->initListen())
    {
        LOG_ERROR(" Create session (" << std::hex << netInfo->getID() << ") failed");
        return false;
    }
    return addSession(session);
}

bool SessionMgr::initConnNet(const std::shared_ptr<ServerNetInfo>& netInfo) {
    if (!netInfo)
    {
        LOG_ERROR("net info is null.");
        return false;
    }

    if (!netInfo->autoInit)
    {
        return true;
    }

    auto session = ServerMgr::get().getSubServ()->createSession(netInfo->sessionType, shared_from_this(), netInfo, eSessionCreateConn);
    if (!session || !session->initConn(false))
    {
        LOG_ERROR(" Create session (" << std::hex << netInfo->getID() << ") failed");
        return false;
    }
    return true;
}

bool SessionMgr::init()
{
    auto cfgMgr = ServerMgr::get().getMgr<ConfigMgr>(ManagerType::eConfig);
    if (!cfgMgr)
    {
        LOG_ERROR("Config manager not found when init session manager.");
        return false;
    }
    const auto& serverCfg = ServerMgr::get().getServerCfg();
    if (!serverCfg)
    {
        LOG_ERROR("Server config not found when init sessin manager.");
        return false;
    }

    // 处理监听端口
    const auto& funcListen = std::bind(&SessionMgr::initListenNet, this, std::placeholders::_1);
    if (!serverCfg->loopListens(funcListen))
    {
        LOG_ERROR("Init listens failed.");
        return false;
    }


    // 处理连接端口
    const auto& funcConn = std::bind(&SessionMgr::initConnNet, this, std::placeholders::_1);
    if (!serverCfg->loopConns(funcConn))
    {
        LOG_ERROR("Init conns failed.");
        return false;
    }

    return true;
}

void SessionMgr::printListenSessions()
{
    for (auto& it : listenSessions_)
    {
        LOG_DEBUG(" listen session id:" << std::hex << it.first << std::dec << it.second->getHandle() );
    }
}

void SessionMgr::printAcceptedSessions()
{
    for (auto& it : acceptedSessions_)
    {
        auto netInfo = it.second->getNetInfo();
        if (!netInfo)
            continue;

        LOG_DEBUG(" listen session id:" << std::hex << netInfo->getID() << std::dec << it.first );
    }
}

void SessionMgr::printConnSessions()
{
    for (auto& it : connSessions_)
    {
        for (auto& its : it.second)
        {
            if (auto& netInfo = its.second->getNetInfo())
                LOG_DEBUG(" listen session id:" << std::hex << netInfo->getID() << std::dec << its.first );
        }
    }
}

/**
 * @desc 拼接SessionID
 * @auth Qiwei.Gu
 * @date 2015-04-14 16:58:10
 */
uint64_t SessionMgr::makeSessionID(ServerType serverType, SessionType sessionType, int16_t serverID)
{
    return ((((uint64_t)serverType) << 48) | ((uint64_t)sessionType << 32) | serverID);
}

std::shared_ptr<Session> SessionMgr::getListenSession(ServerType serverType, SessionType sessionType, int16_t serverID)
{
    std::lock_guard<std::mutex> lock(mutex_);

    const auto& it = listenSessions_.find(makeSessionID(serverType, sessionType, serverID));
    return (it == listenSessions_.end() ? std::shared_ptr<Session>() : it->second);
}

std::shared_ptr<Session> SessionMgr::getAcceptedSession(uint64_t handle)
{
    std::lock_guard<std::mutex> lock(mutex_);
    const auto& it = acceptedSessions_.find(handle);
    return it == acceptedSessions_.end() ? std::shared_ptr<Session>() : it->second;
}

void SessionMgr::getConnSessions(ServerType serverType, SessionType sessionType,
                                 std::vector<std::shared_ptr<Session> >& sessions)
{
    std::lock_guard<std::mutex> lock(mutex_);

    const auto& it = connSessions_.find(std::make_pair(serverType, sessionType));
    if (it == connSessions_.end())
    {
        return;
    }

    for (auto& its : it->second)
    {
        sessions.push_back(its.second);
    }
}


bool SessionMgr::removeSession(const std::shared_ptr<Session>& session)
{
    std::lock_guard<std::mutex> lock(mutex_);

    const auto& netInfo = session->getNetInfo();
    if (!netInfo)
    {
        LOG_ERROR("Session not set net info config, when to remove it.");
        return false;
    }

    switch (session->getCreateType())
    {
    case eSessionCreateListen:
    {
        const auto& it = listenSessions_.find(netInfo->getID());
        if (it == listenSessions_.end())
        {
            LOG_ERROR("Session not found when remove it, id:" << std::hex << netInfo->getID());
            return false;
        }
        it->second->stop();
        listenSessions_.erase(it);
        return true;
    }
    case eSessionCreateAccepted:
    {
        const auto& it = acceptedSessions_.find(session->getHandle());
        if (it == acceptedSessions_.end())
        {
            LOG_ERROR("Session not found when remove it, id:" << std::hex << netInfo->getID());
            return false;
        }
        return true;
    }
    case eSessionCreateConn:
    {
        do
        {
            const auto& it = connSessions_.find(std::make_pair(netInfo->serverType, netInfo->sessionType));
            if (it == connSessions_.end())
            {
                break;
            }
            const auto& its = it->second.find(session->getHandle());
            if (its == it->second.end())
            {
                break;
            }
            its->second->stop();
            it->second.erase(its);
            return true;
        }
        while (false);
        LOG_ERROR("Session not found when remove it, id:" << std::hex << netInfo->getID());
        return false;
    }
    default:
    {
        LOG_ERROR("Session not found when remove it, id:" << std::hex << netInfo->getID());
        return false;
    }
    }
    return false;
}

/**
 * @desc 添加会话
 * @auth Qiwei.Gu
 * @date 2015-03-26 23:25:05
 */
bool SessionMgr::addSession(const std::shared_ptr<Session>& session)
{
    // 保证其同步操作
    std::lock_guard<std::mutex> lock(mutex_);

    const auto& netInfo = session->getNetInfo();
    if (!netInfo)
    {
        LOG_ERROR("Session not set net info config, when added.");
        return false;
    }

    switch (session->getCreateType())
    {
    case eSessionCreateListen:
    {
        listenSessions_[netInfo->getID()] = session;
        return true;
    }
    case eSessionCreateAccepted:
    {
        acceptedSessions_[session->getHandle()] = session;
        return true;
    }
    case eSessionCreateConn:
    {
        connSessions_[std::make_pair(netInfo->serverType, netInfo->sessionType)][session->getHandle()] = session;
        return true;
    }
    default:
    {
        LOG_DEBUG("Add session failed. session create Type not set." << std::hex << netInfo->getID());
        return false;
    }
    }
    return false;
}

bool SessionMgr::send(MessageID msgID, uint32_t sessionID, const google::protobuf::Message& msg)
{
    return true;
}
